﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Space
{
    public class Player
    {
        public int Credit { get; set; }

        public bool Neutral { get; set; }

        public string Name { get; set; }
        public Color Color { get; set; }

        int progressivetaxation(int i)
        {
            if (i <= 20) return i;
            i -= 20;
            if (i <= 20) return (int)(i * 0.8 + 20);
            i -= 20;
            if (i <= 20) return (int)(i * 0.8 + 40);

            i -= 20;
            return (int)(i * 0.8 + 60);
        }

        public int ShipSupportCount
        {
            get
            {
                return progressivetaxation(planets.Sum(p => p.ShipSupportNum));
                //return (planets.Sum(p => p.ShipSupportNum) - 30) / 2 + 30;
            }
        }

        List<CapitalShip> mainFleet = new List<CapitalShip>();
        public List<CapitalShip> MainFleet { get { return mainFleet; } }

        public Planet MainPlanet { get; set; }

        List<Planet> planets = new List<Planet>();

        Game game;
        Game Game { get { return game; } }

        public FlagShip FlagShip { get; set; }

        public List<Positioned> Targets { get; set; }
        public int MaxTarget { get; set; }
        public float TargetRange { get; set; }

        public Player(Game game)
        {
            this.game = game;

            MaxTarget = 3;
            TargetRange = 30;
            Targets = new List<Positioned>(MaxTarget);

            Credit = 1000;
        }

        public void AddPlanet(Planet planet)
        {
            planets.Add(planet);
            if (planets.Count == 1)
            {
                MainPlanet = planet;

                if(FlagShip!=null)
                FlagShip.Position = planets[0].Position + new Vector3(2, 0, 2);
            }
        }

        public void RemovePlanet(Planet planet)
        {
            planets.Remove(planet);
            if (planet == MainPlanet)
            {
                if (planets.Count > 0)
                    MainPlanet = planets[0];
                else MainPlanet = null;
            }
        }

        float updateCooldown;
        
        public void Update(float time)
        {
            FlagShip.Update(time);
            if(MainPlanet!=null)
                MainPlanet.UpdateMainPlanet(time);
            Targets.RemoveAll(p => p is Ship && !(p as Ship).Alive);

            if(MainFleet.Count>0)
                CalculateFormation();
                //lineForm();
        }

        public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison)
        {
            if (list == null)
                throw new ArgumentNullException("list");
            if (comparison == null)
                throw new ArgumentNullException("comparison");

            int count = list.Count;
            for (int j = 1; j < count; j++)
            {
                T key = list[j];

                int i = j - 1;
                for (; i >= 0 && comparison(list[i], key) > 0; i--)
                {
                    list[i + 1] = list[i];
                }
                list[i + 1] = key;
            }
        }

        void CalculateFormation()
        {
            Vector3 relativePosition = Vector3.Zero;

            //MainFleet.Sort((a, b) => a.Class - b.Class);
            InsertionSort(MainFleet, (a, b) => a.Class - b.Class);

            int clas = int.MinValue;
            float forward = -1;

            int classStartIndex = 0;
            int classCountNumber = 0;

            float distMult = 6;
            int i;
            for (i = 0; i < MainFleet.Count; i++)
            {
                CapitalShip ship = MainFleet[i];
                int c = (int)ship.Class;
                if (forward == -1 && c > 0)
                {
                    forward = 1;
                    relativePosition.Z = 0;
                }
                if (clas == int.MinValue || clas != (int)ship.Class)
                {
                    if (i!=0&&(classCountNumber & 1) == 0)
                    {
                        bool left = MainFleet[i-1].FormationPoint.X >0;
                        for (int j = classStartIndex; j < i; j++)
                            MainFleet[j].FormationPoint += new Vector3((left ? -1 : 1) * MainFleet[i - 1].Radius * distMult/2, 0, 0);
                    }
                    clas = (int)ship.Class;
                    relativePosition.Z += forward * ship.Radius * distMult;
                    relativePosition.X = 0;
                    classStartIndex = i;
                    classCountNumber = 1;
                }
                else
                {
                    classCountNumber++;
                    if (relativePosition.X == 0)
                        relativePosition.X = ship.Radius * distMult;
                    else if (relativePosition.X < 0)
                    {
                        relativePosition.X = -relativePosition.X + ship.Radius * distMult;
                    }
                    else
                    {
                        relativePosition.X = -relativePosition.X;
                    }
                }
                ship.FormationPoint = relativePosition;
                //ship.Position = ship.FormationPoint = Vector3.Transform(relativePosition, Matrix.CreateRotationY((float)(Math.PI * 6 / 4 + Math.PI)-FlagShip.Angle)) + FlagShip.Position;
            }
            if (i != 0 && (classCountNumber & 1) == 0)
            {
                bool left = MainFleet[i - 1].FormationPoint.X > 0;
                for (int j = classStartIndex; j < i; j++)
                    MainFleet[j].FormationPoint += new Vector3((left ? -1 : 1) * MainFleet[i - 1].Radius * distMult/2, 0, 0);
            }

            MainFleet.ForEach(ship => ship.FormationPoint = Vector3.Transform(ship.FormationPoint, Matrix.CreateRotationY((float)(Math.PI * 6 / 4 + Math.PI) - FlagShip.Angle)) + FlagShip.Position);
        }

        void lineForm()
        {
            MainFleet.Sort((sa, sb) => sa.Class - sb.Class);
            List<CapitalShip> its = MainFleet;
            Vector3 p = FlagShip.Position;

            Vector3[] positions = new Vector3[its.Count];

            double area = 0;
            foreach (CapitalShip i in its)
            {
                area += (((i.Radius * 4) * (i.Radius * 4)));
            }
            double a = Math.Sqrt(1.618 * area);

            float x_pos = 0;
            float y_pos = 0;

            //		String temp=its[0].getName();
            double temp = its[0].Radius;
            int counter = 0;

            positions[0] = new Vector3(x_pos, p.Y, y_pos);

            for (int i = 1; i < its.Count; i++, counter++)
            {
                float dist = (its[i].Radius * 4);
                //			if(!its[i].getName().equals(temp)) {
                if (its[i].Radius != temp)
                {
                    x_pos = 0;
                    y_pos += dist;
                    counter = -1;
                }
                //			temp=its[i].getName();
                temp = its[i].Radius;

                if (counter % 2 == 0)
                {
                    if ((Math.Abs(x_pos) + dist) <= (a / 2)) x_pos += (x_pos < 0 ? -dist : dist);
                    else
                    {
                        x_pos = 0;
                        y_pos += dist;
                        counter = -1;
                    }
                }
                else
                {
                    if (x_pos == 0) x_pos += dist;
                    else x_pos = -x_pos;
                }
                positions[i] = new Vector3(x_pos, p.Y, y_pos);
            }
            order(p, positions, false);
        }
        void order(Vector3 p, Vector3[] positions, bool ordered_position)
        {
            List<CapitalShip> its = MainFleet;

            float alpha = (FlagShip.Angle - (float)(Math.PI * 6 / 4));
            if (alpha < 0) alpha += (float)(2 * Math.PI);
            float c_angle = (float)Math.Cos(alpha);
            float s_angle = (float)Math.Sin(alpha);
            for (int i = 0; i < positions.Length; i++)
            {
                float x2 = positions[i].X;
                float y2 = positions[i].Z;

                float x3 = x2 * c_angle - y2 * s_angle;
                float y3 = x2 * s_angle + y2 * c_angle;
                //, positions[i].angle
                positions[i] = new Vector3(x3 + p.X, Math.Max(p.Y, 0), y3 + p.Z);
            }

            for (int i = 0; i < its.Count - 1; i++)
            {
                for (int j = i + 1; j < its.Count; j++)
                {
                    if ((its[j].Position - p).LengthSquared() > (its[i].Position - p).LengthSquared())
                    {
                        CapitalShip temp = its[i];
                        its[i] = its[j];
                        its[j] = temp;

                        Vector3 temp2 = positions[i];
                        positions[i] = positions[j];
                        positions[j] = temp2;
                    }
                }
            }
            //		Arrays.sort(its, new Comparator<Selectable>() {
            //			public int compare(Selectable a, Selectable b) {
            //				return -(int)(a.distance(p)-b.distance(p));
            //			}
            //		});
            for (int i = 0; i < its.Count; i++)
            {
                if (its[i] != null)
                {
                    //				ordered_position=true;
                    int dest = ordered_position ? i : closestSameTypePositionIndex(its, positions, i);//its[i].getVector3().closestVector3(positions);
                    its[i].FormationPoint = positions[dest];
                    positions[dest].X = float.NaN;
                }
            }
        }
        int closestSameTypePositionIndex(List<CapitalShip> its, Vector3[] ps, int index)
        {
            CapitalShip select = its[index];
            Vector3 itemPosition = select.Position;

            int re = -1;
            double d = 0;
            for (int i = 0; i < ps.Length; i++)
                if (!float.IsNaN(ps[i].X))
                    if (select.Class == its[i].Class && (re == -1 || (itemPosition - ps[i]).Length() < d))
                    {
                        re = i;
                        d = (itemPosition - ps[i]).Length();
                    }
            return re;
        }

        public void Plot(float time) {
            updateCooldown -= time;
            if (updateCooldown < 0)
            {
                Vector3 startPosition;
                if (planets.Count > 0) startPosition = planets[0].Position;
                else startPosition = FlagShip.Position;

                float dist = 0;
                Planet pt = null;
                foreach(SolarSystem s in game.Services.GetService<IData>().SolarSystems)
                    foreach (Planet p in s.Planets.Where(p => p.Player != this && p.Player.Neutral && !MainFleet.Exists(ship => ship.Action == "colonize" && ship.ActionTarget == p)))
                    {
                        float d = (p.Position - startPosition).LengthSquared();
                        if (d < dist || pt == null)
                        {
                            dist = d;
                            pt = p;
                        }
                    }
            
                if (pt!=null)
                {
                    Planet planet = pt;


                    //FlagShip.TargetSpeed = FlagShip.Speed;
                    //Vector3 dif = planet.Position - FlagShip.Position;
                    //FlagShip.TargetAngle = (float)Math.Atan2(dif.Z, dif.X);

                    if ((FlagShip.Position - planet.Position).Length() <= C.ColonizeDistance)
                    {
                        Colonize(planet);
                    }
                    else
                    {
                        FlagShip.SetDestination(planet.Position);
                    }

                    //if (fleets[0].Size > planet.MaxResistance + 3)
                    //    fleets[0].Position = planet.Position;
                    //else if (fleets[0].Size < planet.Resistance)
                    //    if (planets.Count > 0)
                    //        fleets[0].Position = planets[0].Position + new Vector3(1, 0, 1);
                }
                updateCooldown = 0;// (float)Game1.Rand.NextDouble() * 5;
            }
        }

        public void BuyShip(string ship)
        {
            IData data = Game.Services.GetService<IData>();
            CapitalShip s = (CapitalShip)Ship.CreateShip(Game, ship);
            if (SpendCredits(s.CreditCost))
            {
                s.Built = false;
                s.Player = this;
                MainFleet.Add(s);
            }
        }

        public void BuyPirate(string ship)
        {
            IData data = Game.Services.GetService<IData>();
            PirateShip s = (PirateShip)Ship.CreateShip(Game, ship);
            if (SpendCredits(s.CreditCost))
            {
                s.Position = FlagShip.Position;
                s.Player = this;
                data.AddShip(s);
            }
        }

        public void Colonize(Planet planet)
        {
            IData data = Game.Services.GetService<IData>();
            foreach (SolarSystem s in data.SolarSystems)
            {
                if (planet != null)
                {
                    CapitalShip ship = (CapitalShip)Ship.CreateShip(Game, "colony");
                    if (SpendCredits(ship.CreditCost))
                    {
                        ship.Built = false;
                        ship.Player = this;
                        MainFleet.Add(ship);

                        ship.SetAction("colonize", planet);
                    }
                    break;
                }
            }
        }

        public void Build(string building)
        {
            IData data = Game.Services.GetService<IData>();
            SolarSystem solar = data.SolarSystems.OrderBy(s => (s.Position - FlagShip.Position).Length()).First();
            int oldInd = solar.Buildings.FindIndex(b => b.Player == this && b.Category == building);
            Building existingBuilding = oldInd >= 0 ? solar.Buildings[oldInd] : null;

            int level;
            if (existingBuilding == null) level = 1;
            else level = existingBuilding.Level + 1;

            if (level == 1)
            {
                Building b = Building.Create(Game, building, level);
                b.Position = FlagShip.Position;
                b.SolarSystem = data.SolarSystems.OrderBy(s => (s.Position - FlagShip.Position).Length()).First();
                b.Player = this;

                b.SolarSystem.AddBuilding(b);

            }
            else
            {
                Building b = Building.Create(Game, building, level);
                b.Player = this;

                b.Position = existingBuilding.Position;
                b.SolarSystem = solar;

                solar.Buildings[oldInd] = b;
            }
        }

        public void PickTargets()
        {
            Targets.Clear();

            var v = from ship in game.Services.GetService<IData>().Ships
                    where ship.Player != this && (ship.Position - FlagShip.Position).Length() < TargetRange
                    select ship;

            foreach(Ship s in v) {
                Targets.Add(s);
                if (Targets.Count == MaxTarget) break;
            }
        }

        public bool SpendCredits(int credit)
        {
            if (Credit < credit) return false;
            Credit -= credit;
            return true;
        }
    }
}
